package org.common.utils.database.access;

import org.common.utils.function.FunctionThrows;
import org.common.utils.function.TriConsumer;
import org.common.utils.json.JSONTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 数据库操作
 * @author zhouzhibing
 * @date 2025/4/4
 */
public class DataAccess{

    private static final Logger log = LoggerFactory.getLogger(DataAccess.class);

    /**
     * 数据源
     */
    protected final DataSource dataSource;

    public DataAccess(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * 执行插入
     * @param object 插入对象
     */
    public void insert(Object object) {
        insert(List.of(object));
    }

    /**
     * 执行插入
     * @param insertList 插入对象列表
     */
    public void insert(List<?> insertList) {
        executeStatement(insertList , SqlTemplate::getInsertSqlTemplate , SqlTemplate::insertStatementParameter);
    }

    /**
     * 执行更新
     * @param object 更新对象
     */
    public void update(Object object) {
        update(List.of(object));
    }

    /**
     * 执行更新
     * @param updateList 更新对象列表
     */
    public void update(List<?> updateList) {
        executeStatement(updateList , SqlTemplate::getUpdateSqlTemplate , SqlTemplate::updateStatementParameter);
    }

    /**
     * 执行删除
     * @param object 删除对象
     */
    public void delete(Object object) {
        delete(List.of(object));
    }

    /**
     * 执行删除
     * @param deleteList 删除对象列表
     */
    public void delete(List<?> deleteList) {
        executeStatement(deleteList , SqlTemplate::getDeleteSqlTemplate , SqlTemplate::deleteStatementParameter);
    }

    public <T> List<T> select(Class<T> clzss) {
        return select(clzss , null , null);
    }

    /**
     * 执行查询
     * @param clzss 实体类型
     * @param whereString where条件字符串
     * @param params 参数列表
     * @return 实体列表
     * @param <T> 泛型
     */
    public <T> List<T> select(Class<T> clzss , String whereString , List<Object> params) {
        SqlTemplate sqlTemplate = SqlTemplate.getSqlTemplate(clzss);
        String sqlString = whereString == null ? sqlTemplate.getSelectSqlTemplate() : sqlTemplate.getSelectSqlTemplate() + whereString;
        log.info("DataAccess.select(Class, String, List) sqlString = {} , params = {}" , sqlString , JSONTool.toJSONString(params));
        return select(sqlString , params , sqlTemplate::newInstance);
    }

    /**
     * 执行查询
     * @param sqlString where条件字符串
     * @param function 结果集处理
     * @return 实体列表
     * @param <T> 泛型
     */
    public <T> List<T> select(String sqlString , FunctionThrows<ResultSet , T> function) {
        log.info("DataAccess.select(String , FunctionThrows) sqlString = {}" , sqlString);
        return select(sqlString , null , function);
    }

    /**
     * 执行查询
     * @param sqlString sql语句
     * @param params 参数列表
     * @param function 结果集处理
     * @return 返回对应查询的列表
     * @param <T> 泛型
     */
    public <T> List<T> select(String sqlString , List<Object> params , FunctionThrows<ResultSet , T> function) {
        try(Connection connection = dataSource.getConnection()) {
            PreparedStatement statement = connection.prepareStatement(sqlString , ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            if(params != null) {
                for (int i = 0; i < params.size(); i++) {
                    statement.setObject(i + 1 , params.get(i));
                }
            }
            ResultSet resultSet = statement.executeQuery();
            resultSet.last();
            int size = resultSet.getRow();
            List<T> list = new ArrayList<>(size);
            resultSet.beforeFirst();

            while (resultSet.next()) {
                T instance = function.apply(resultSet);
                list.add(instance);
            }
            return list;
        } catch (Exception e) {
            log.error("DataAccess.select exception , sqlString = {} , params = {}" , sqlString , JSONTool.toJSONString(params) , e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行批量插入、更新、删除
     * @param instanceList 实体对象列表
     * @param function sql模板函数
     * @param consumer 参数设置函数
     */
    private void executeStatement(List<?> instanceList , Function<SqlTemplate, String> function , TriConsumer<SqlTemplate, PreparedStatement , Object> consumer) {
        //分类列表 <实体类型，<实体对象列表>>
        HashMap<Class<?> , List<Object>> objectMap = new HashMap<>();
        for (Object object : instanceList) {
            List<Object> newList = objectMap.computeIfAbsent(object.getClass(), (k) -> new ArrayList<>());
            newList.add(object);
        }

        String currentSqlString = null;
        List<PreparedStatement> statementList = new ArrayList<>(objectMap.size());
        try(Connection connection = dataSource.getConnection()) {
            for (Map.Entry<Class<?>, List<Object>> entry : objectMap.entrySet()) {
                Class<?> clzss = entry.getKey();
                List<Object> list = entry.getValue();
                SqlTemplate sqlTemplate = SqlTemplate.getSqlTemplate(clzss);
                String sqlTemplateString = function.apply(sqlTemplate);
                currentSqlString = sqlTemplateString;

                log.info("DataAccess.executeStatement sqlString = {}" , sqlTemplateString);
                PreparedStatement statement = connection.prepareStatement(sqlTemplateString);
                statementList.add(statement);

                for (Object instance : list) {
                    consumer.accept(sqlTemplate , statement, instance);
                    statement.addBatch();
                }

                int[] batchResult = statement.executeBatch();
                if (batchResult.length != list.size()) {
                    log.warn("DataAccess.executeStatement batchCount error sql = {} , list {}", currentSqlString, list);
                }

                for (int result : batchResult) {
                    if (result == Statement.EXECUTE_FAILED) {
                        log.warn("DataAccess.executeStatement execute fail sql = {} , list {}", currentSqlString, list);
                    }
                }
            }

            for (PreparedStatement statement : statementList) {
                statement.executeBatch();
            }
        } catch (SQLException e) {
            log.error("DataAccess.executeStatement exception , currentSqlString = {}" , currentSqlString , e);
            throw new RuntimeException(e);
        }
    }
}
